package com.mls.guardian.appst.service;

import com.mls.guardian.appst.AppController;
import com.mls.guardian.appst.chain.TaskExecutor;
import com.mls.guardian.appst.chain.TaskInvoker;
import com.mls.guardian.appst.chain.TaskResult;
import com.mls.guardian.appst.chain.excutor.*;
import com.mls.guardian.appst.chain.excutor.Upgrade.GitPullExcutor;
import com.mls.guardian.appst.chain.excutor.Upgrade.ReleaseToTarExcutor;
import com.mls.guardian.appst.chain.excutor.Upgrade.RestartExcutor;
import com.mls.guardian.appst.chain.excutor.Upgrade.UnzipExcutor;
import com.mls.guardian.appst.common.BackupUtil;
import com.mls.guardian.common.GitUtil;
import com.mls.guardian.common.SvnUtil;
import com.mls.guardian.config.AppSTConfig;
import com.mls.guardian.config.ProjectConfig;
import com.mls.guardian.config.helper.ConfigHandler;
import com.mls.guardian.config.helper.ConfigPathManagr;
import com.mls.guardian.config.helper.JsonConfigHelper;
import com.mls.guardian.dataobj.BackupProjectObj;
import com.mls.guardian.dataobj.ModuleMessage;
import com.mls.guardian.dataobj.ProcessMessage;
import com.mls.guardian.em.ContainerRole;
import com.mls.guardian.em.ModuleType;
import com.mls.guardian.em.RevisionType;
import com.mls.guardian.protocol.body.AppRestartBody;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.*;

import static com.mls.guardian.em.RevisionType.SVN;

/**
 * Created by jl on 15/10/27.
 */
public class RestartAppService {
    private static final Logger log = LoggerFactory.getLogger(RestartAppService.class);
    private AppController appController;
    private AppSTConfig appSTConfig;
    private Map<String, List> taskListMap = new HashMap<String, List>();
    private static Map<String, Class<? extends TaskExecutor>> taskExecutorMap = new HashMap<String, Class<? extends TaskExecutor>>();
    private List<ProjectConfig> projectObjList = new ArrayList();
    private String versionTag;

    static {
        taskExecutorMap.put("backupJar", BackupJarExcutor.class);
        taskExecutorMap.put("revisionUpdate", RevisionUpdateExcutor.class);
        taskExecutorMap.put("compileJar", CompileJarExcutor.class);
        taskExecutorMap.put("copyJar", CopyJarExcutor.class);
        taskExecutorMap.put("moveJar", MoveJarExcutor.class);
        taskExecutorMap.put("restartJar", RestartJarExcutor.class);
        taskExecutorMap.put("restoreJar", RestoreJarExcutor.class);

        taskExecutorMap.put("backupWeb", BackupWebExcutor.class);
        taskExecutorMap.put("compileWeb", CompileWebExcutor.class);
        taskExecutorMap.put("copyWeb", CopyWebExcutor.class);
        taskExecutorMap.put("moveWeb", MoveWebExcutor.class);
        taskExecutorMap.put("unzipWar", UnzipWarExcutor.class);
        taskExecutorMap.put("replaceJs", ReplaceJsExcutor.class);
        taskExecutorMap.put("restartTomcat", RestartTomcatExcutor.class);
        taskExecutorMap.put("restoreWeb", RestoreWebExcutor.class);

        taskExecutorMap.put("stopProcess", StopProcessExcutor.class);
        taskExecutorMap.put("clean", CleanExcutor.class);
    }

    public RestartAppService(AppController appController, AppSTConfig appSTConfig) {
        this.appController = appController;
        this.appSTConfig = appSTConfig;
    }

    public void setAppSTConfig(AppSTConfig appSTConfig) {
        this.appSTConfig = appSTConfig;
    }


    public void init(List<ProjectConfig> projectObjList) throws Exception {
        this.projectObjList = projectObjList;
        parseInvoker();

        log.info("restart service init ok");
    }

    /**
     * startApp
     *
     * @param projectName
     * @param projectList
     * @return
     * @throws Exception
     */
    public AppRestartBody startApp(String projectName, List<ProcessMessage> projectList) throws Exception {
        AppRestartBody appRestartResult = new AppRestartBody();

        ProjectConfig restartProject = findProject(projectName, projectList);

        Optional<ProjectConfig> op = this.projectObjList.stream().filter(x -> x.getProjectName().equals(restartProject.getProjectName())).findFirst();

        if (op.isPresent()) {
            if (restartProject.getJarModuleList() == null && restartProject.getWebModuleList() == null) {
                appRestartResult.setSuc(true);
                return appRestartResult;

            } else {
                ProjectConfig matchProject = op.get();
                //jar
                if (restartProject.getJarModuleList() != null) {
                    List<String> list = restartProject.getJarModuleList();
                    for (String app : list) {
                        TaskResult result = startApp(matchProject, app);

                        if (!result.isSuc()) {
                            return task2AppRestart(result);
                        }
                    }

                }

                //tomcat
                if (restartProject.getWebModuleList() != null && restartProject.getWebModuleList().size() > 0) {
                    TaskResult result = startApp(matchProject, "tomcat");

                    if (!result.isSuc()) {
                        return task2AppRestart(result);
                    }

                }
            }
        }


        appRestartResult.setSuc(true);
        return appRestartResult;
    }

    /**
     * stopApp
     *
     * @param projectName
     * @param projectList
     * @return
     * @throws Exception
     */
    public AppRestartBody stopApp(String projectName, List<ProcessMessage> projectList) throws Exception {
        AppRestartBody appRestartResult = new AppRestartBody();

        ProjectConfig restartProject = findProject(projectName, projectList);

        Optional<ProjectConfig> op = this.projectObjList.stream().filter(x -> x.getProjectName().equals(restartProject.getProjectName())).findFirst();

        if (op.isPresent()) {
            if (restartProject.getJarModuleList() == null && restartProject.getWebModuleList() == null) {
                appRestartResult.setSuc(true);
                return appRestartResult;

            } else {
                ProjectConfig matchProject = op.get();
                //jar
                if (restartProject.getJarModuleList() != null) {
                    List<String> list = restartProject.getJarModuleList();
                    for (String app : list) {
                        TaskResult result = stopApp(matchProject, app);

                        if (!result.isSuc()) {
                            return task2AppRestart(result);
                        }
                    }

                }

                //tomcat
                if (restartProject.getWebModuleList() != null && restartProject.getWebModuleList().size() > 0) {
                    TaskResult result = stopApp(matchProject, "tomcat");

                    if (!result.isSuc()) {
                        return task2AppRestart(result);
                    }

                }
            }
        }


        appRestartResult.setSuc(true);
        return appRestartResult;
    }


    private ProjectConfig findProject(String projectName, List<ProcessMessage> willHandleAppList) {
        ProjectConfig projectRet = null;
        for (ProjectConfig projectObj : this.projectObjList) {
            if (projectObj.getProjectName().equals(projectName)) {
                projectRet = new ProjectConfig();
                projectRet.setProjectName(projectName);
                projectRet.setProjectPath(projectObj.getProjectPath());
                projectRet.setRevisionAddress(projectObj.getRevisionAddress());
                projectRet.setRevisionType(projectObj.getRevisionType());

                List<String> moduleList = new ArrayList<String>();
                List<String> appList = new ArrayList<String>();
                for (ModuleMessage appMessage : willHandleAppList) {
                    if (projectObj.getWebModuleList().contains(appMessage.getModuleName())) {
                        //if (processObj.getProcessName().equals("tomcat")) {
                        appList.add(appMessage.getModuleName());
                    } else {
                        moduleList.add(appMessage.getModuleName());
                    }
                }


                projectRet.setJarModuleList(moduleList);
                projectRet.setWebModuleList(appList);
            }
        }

        return projectRet;
    }

    public AppRestartBody restartApp(String projectName, List<ProcessMessage> projectList, String versionTag) throws Exception {
        this.versionTag = versionTag;

        AppRestartBody appRestartResult = new AppRestartBody();

        ProjectConfig restartProject = findProject(projectName, projectList);

        Optional<ProjectConfig> op = this.projectObjList.stream().filter(x -> x.getProjectName().equals(restartProject.getProjectName())).findFirst();

        if (op.isPresent()) {
            if (restartProject.getJarModuleList() == null && restartProject.getWebModuleList() == null) {
                TaskResult result = restartProjectAll(op.get());

                if (!result.isSuc()) {
                    return task2AppRestart(result);
                }

            } else {
                ProjectConfig matchProject = op.get();
                //dubbo服务
                if (restartProject.getJarModuleList() != null && restartProject.getJarModuleList().size() > 0) {
                    List<String> list = restartProject.getJarModuleList();
                    for (String app : list) {
                        TaskResult result = restartJar(matchProject, app);

                        if (!result.isSuc()) {
                            return task2AppRestart(result);
                        }
                    }

                }

                //tomcat服务,只要勾选一个就全部重启
                if (restartProject.getWebModuleList() != null && restartProject.getWebModuleList().size() > 0) {
                    List<String> list = restartProject.getWebModuleList();
                    TaskResult result = restartWeb(matchProject, list);

                    if (!result.isSuc()) {
                        return task2AppRestart(result);
                    }

                }
            }
        }


        appRestartResult.setSuc(true);
        /**
         * TODO:返回的版本号 也应该是根据不同的版本控制
         */
        if(restartProject.getRevisionType().equals(RevisionType.SVN)){
            appRestartResult.setVersion(SvnUtil.getLastVersion(restartProject.getRevisionAddress(), this.appSTConfig.getSvnUserName(), this.appSTConfig.getSvnPassword()));
        }else{
            appRestartResult.setVersion(GitUtil.getLastVersion(restartProject.getProjectPath()));
        }
       return appRestartResult;
    }


    public AppRestartBody task2AppRestart(TaskResult taskResult) {
        AppRestartBody appRestart = new AppRestartBody();
        appRestart.setErrorContent(taskResult.getError());
        appRestart.setSuc(taskResult.isSuc());

        return appRestart;
    }

    public TaskResult restartJar(ProjectConfig project, String moduleName) throws Exception {
        log.info("开始执行重启jar任务 应用是[{}] 路径是[{}]", moduleName, project.getProjectPath() + File.separator + moduleName);
        TaskInvoker invoker = getInvoker("jar", project, moduleName);
        invoker.setProject(project);
        TaskResult result = invoker.invoke();

        return result;
    }

    public TaskResult startApp(ProjectConfig project, String moduleName) throws Exception {
        log.info("开始执行start任务 应用是[{}] 路径是[{}]", moduleName, project.getProjectPath() + File.separator + moduleName);

        TaskInvoker invoker = new TaskInvoker(project, this.appSTConfig, this.versionTag);
        invoker.setProject(project);
        if (project.getJarModuleList().contains(moduleName)) {
            invoker.addModule(moduleName, ModuleType.JAR);

            //清理数据
            TaskExecutor task = new CleanExcutor();
            invoker.setExecutor(task);

            //start-jar
            task = new RestartJarExcutor();
            invoker.setExecutor(task);
        } else {
            invoker.addModule(moduleName, ModuleType.WEB);

            //清理数据
            TaskExecutor task = new CleanExcutor();
            invoker.setExecutor(task);

            //start-tomcat
            task = new RestartTomcatExcutor();
            invoker.setExecutor(task);
        }


        return invoker.invoke();
    }

    public TaskResult stopApp(ProjectConfig project, String moduleName) throws Exception {
        log.info("开始执行kill process任务 应用是[{}] 路径是[{}]", moduleName, project.getProjectPath() + File.separator + moduleName);

        TaskInvoker invoker = new TaskInvoker(project, this.appSTConfig, this.versionTag);
        invoker.setProject(project);
        if (project.getJarModuleList().contains(moduleName)) {
            invoker.addModule(moduleName, ModuleType.JAR);
        } else {
            invoker.addModule(moduleName, ModuleType.WEB);
        }

        //清理数据
        TaskExecutor task = new CleanExcutor();
        invoker.setExecutor(task);

        //停止进程
        task = new StopProcessExcutor();
        invoker.setExecutor(task);


        return invoker.invoke();
    }

    public AppRestartBody updateStaticFile(String projectName, List<ProcessMessage> willHandleAppList) throws Exception {
        AppRestartBody appRestartResult = new AppRestartBody();

        ProjectConfig restartProject = findProject(projectName, willHandleAppList);

        Optional<ProjectConfig> op = this.projectObjList.stream().filter(x -> x.getProjectName().equals(restartProject.getProjectName())).findFirst();

        if (op.isPresent()) {
            ProjectConfig matchProject = op.get();
            if (restartProject.getWebModuleList() != null || restartProject.getWebModuleList().size() > 0) {
                List<ModuleMessage> moduleList = new ArrayList<ModuleMessage>();
                //只针对Web模块
                for (String item : restartProject.getWebModuleList()) {
                    Optional<ProcessMessage> optional = willHandleAppList.stream().filter(t -> t.getModuleName().equals(item)).findFirst();
                    if (optional.isPresent()) {
                        moduleList.add(optional.get());
                    }
                }
                TaskResult result = updateStaticFile(matchProject, moduleList);

                if (!result.isSuc()) {
                    return task2AppRestart(result);
                }

            }
        }


        appRestartResult.setSuc(true);
        return appRestartResult;
    }

    public TaskResult updateStaticFile(ProjectConfig project, List<ModuleMessage> moduleList) throws Exception {
        log.info("----开始执行updateStaticFile任务----");

        TaskInvoker invoker = new TaskInvoker(project, moduleList, this.appSTConfig, this.versionTag);
        invoker.setProject(project);

        //清理数据
        TaskExecutor task = new CleanExcutor();
        invoker.setExecutor(task);

        //下载代码
        task = new RevisionUpdateExcutor();
        invoker.setExecutor(task);

        //更新静态文件
        task = new UpdateStaticFileExcutor();
        invoker.setExecutor(task);

        //替换js
        task = new ReplaceJsExcutor();
        invoker.setExecutor(task);


        return invoker.invoke();
    }

    public List<BackupProjectObj> getBackup(String projectName, String versionTag) throws Exception {
        log.info("开始执行getBackup任务 应用是[{}] 备份tag是[{}]", projectName, versionTag);
        return BackupUtil.getInstance(this.appSTConfig).getBackup(projectName, versionTag);
    }

    public TaskResult restartWeb(ProjectConfig project, List<String> moduleNameList) throws Exception {
        log.info("开始执行重启app任务 应用是[{}] 路径是[{}]", "", project.getProjectPath() + File.separator);
        TaskInvoker invoker = getInvoker("web", project, null);
        invoker.setProject(project);
        if (moduleNameList == null || moduleNameList.size() == 0) {
            invoker.addModule("tomcat", ModuleType.WEB);
        } else {
            for (String item : moduleNameList) {
                invoker.addModule(item, ModuleType.WEB);
            }
        }

        TaskResult result = invoker.invoke();

        return result;
    }


    public TaskInvoker getInvoker(String key, ProjectConfig project, String moduleName) throws Exception {
        TaskInvoker invoker = new TaskInvoker(project, this.appSTConfig, this.versionTag);
        if (moduleName != null && moduleName.trim().length() > 0) {
            if (project.getJarModuleList().contains(moduleName)) {
                invoker.addModule(moduleName, ModuleType.JAR);
            } else {
                invoker.addModule(moduleName, ModuleType.WEB);
            }
        }
        List<Class> list = taskListMap.get(key);

        for (Class clz : list) {
            TaskExecutor task = (TaskExecutor) clz.newInstance();
            invoker.setExecutor(task);
        }

        return invoker;
    }

    public TaskResult restartProjectAll(ProjectConfig restartProject) throws Exception {

        log.info("开始执行重启[{}]任务 路径是[{}]", restartProject.getProjectName(), restartProject.getProjectPath());
        TaskResult resultRet = new TaskResult();
        resultRet.setSuc(true);

        List<String> sList = restartProject.getJarModuleList();
        for (String app : sList) {
            TaskResult result = restartJar(restartProject, app);

            if (!result.isSuc()) {
                return result;
            }
        }


        TaskResult result = restartWeb(restartProject, restartProject.getWebModuleList());

        if (!result.isSuc()) {
            return result;
        }


        return resultRet;
    }

    public AppRestartBody restoreApp(String projectName, String versionTag) throws Exception {
        AppRestartBody appRestoreResult = new AppRestartBody();
        ProjectConfig restoreProject = null;
        for (ProjectConfig item : this.projectObjList) {
            if (item.getProjectName().equals(projectName)) {
                restoreProject = item;
                break;
            }
        }
        if (restoreProject == null) {
            throw new Exception("未指定需要还原的应用名");
        }

        Optional<ProjectConfig> op = this.projectObjList.stream().
                filter(x -> x.getProjectName().equals(projectName)).
                findFirst();
        if (op.isPresent()) {
            ProjectConfig matchProject = op.get();

            //restoreJar,内部循环处理
            if (restoreProject.getJarModuleList() != null) {
                List<String> list = restoreProject.getJarModuleList();
                TaskResult result = restoreJar(matchProject, list, versionTag);

                if (!result.isSuc()) {
                    return task2AppRestart(result);
                }
            }
            //restoreWeb,处理一次
            if (restoreProject.getWebModuleList() != null) {
                List<String> list = restoreProject.getWebModuleList();
                if (list != null && list.size() > 0) {
                    TaskResult result = restoreWeb(matchProject, list, versionTag);

                    if (!result.isSuc()) {
                        return task2AppRestart(result);
                    }
                }
            }
        }


        appRestoreResult.setSuc(true);
        return appRestoreResult;
    }

    public TaskResult restoreJar(ProjectConfig project, List<String> moduleNameList, String versionTag) throws Exception {
        log.info("----开始执行restoreJar任务----");
        TaskInvoker invoker = new TaskInvoker(project, this.appSTConfig, this.versionTag);
        invoker.setVersionTag(versionTag);
        for (String item : moduleNameList) {
            invoker.addModule(item, ModuleType.JAR);
        }
        //清理数据
        TaskExecutor task = new CleanExcutor();
        invoker.setExecutor(task);

        //停止dubbo服务
        task = new StopProcessExcutor();
        invoker.setExecutor(task);

        //从备份目录复制jar文件替换目标目录jar文件
        task = new RestoreJarExcutor();
        invoker.setExecutor(task);

        //重启dubbo服务
        task = new RestartJarExcutor();
        invoker.setExecutor(task);

        return invoker.invoke();
    }

    public TaskResult restoreWeb(ProjectConfig project, List<String> moduleNameList, String versionTag) throws Exception {
        log.info("----开始执行restoreWeb任务----");
        TaskInvoker invoker = new TaskInvoker(project, this.appSTConfig, this.versionTag);
        invoker.setVersionTag(versionTag);
        for (String item : moduleNameList) {
            invoker.addModule(item, ModuleType.WEB);
        }
        //清理数据
        TaskExecutor task = new CleanExcutor();
        invoker.setExecutor(task);


        //停止tomcat服务
        task = new StopProcessExcutor();
        invoker.setExecutor(task);

        //war文件从备份目录复制到程序启动目录
        task = new RestoreWebExcutor();
        invoker.setExecutor(task);

        //删除目录,解压文件
        task = new UnzipWarExcutor();
        invoker.setExecutor(task);

        //复制js
        task = new ReplaceJsExcutor();
        invoker.setExecutor(task);

        //重启tomcat服务
        task = new RestartTomcatExcutor();
        invoker.setExecutor(task);


        return invoker.invoke();
    }

    public TaskResult upgrade(ContainerRole role, String upgradeVersion) throws Exception {
        log.info("----开始执行Upgrade Guardian任务----");
        TaskInvoker invoker = new TaskInvoker(null, this.appSTConfig, upgradeVersion);
        invoker.setRole(role);

        //清理数据
        TaskExecutor task = new CleanExcutor();
        invoker.setExecutor(task);

        //Git pull获取代码
        task = new GitPullExcutor();
        invoker.setExecutor(task);

        //ReleaseToTar打包代码
        task = new ReleaseToTarExcutor();
        invoker.setExecutor(task);

        //复制解压
        task = new UnzipExcutor();
        invoker.setExecutor(task);

        //重起程序
        task = new RestartExcutor();
        invoker.setExecutor(task);


        return invoker.invoke();
    }

    private void parseInvoker() throws Exception {
        ConfigHandler jsonConfigHelper = new JsonConfigHelper();
        Map<String, List<String>> map = (Map<String, List<String>>) jsonConfigHelper.parse(ConfigPathManagr.getAppSTConfigPath());

        for (Map.Entry<String, List<String>> entry : map.entrySet()) {
            String key = entry.getKey();
            List<String> taskList = entry.getValue();

            List list = parseTaskExecutor(taskList);
            taskListMap.put(key, list);
        }
    }

    private List parseTaskExecutor(List<String> taskList) throws Exception {
        List<Class> list = new ArrayList();

        for (String task : taskList) {
            if (taskExecutorMap.get(task) != null) {
                Class clz = taskExecutorMap.get(task);
                list.add(clz);
            } else {
                throw new RuntimeException(task + " 并没有对应的解析器 请检查配置文件appSt.json");
            }
        }

        return list;
    }
}
